package stencil.core.engine.scene 
{
	import flash.geom.Rectangle;
	import stencil.core.engine.IGrid;
	import stencil.core.engine.IMapArea;
	import stencil.core.engine.ISceneObject;
	import stencil.core.engine.MapNode;
	import stencil.core.engine.SimpleMapArea;
	import stencil.core.engine.utils.DataGrid;
	
	/**
	 * ...
	 * @author Max Zhuravkov
	 */
	public class GridBasedSceneStructure extends AbstractSceneStructure {
		
		private var _dataGrid:DataGrid;
		
		//------------------------------------------------------------------------------------------------
		// Constructor
		//------------------------------------------------------------------------------------------------
		
		public function GridBasedSceneStructure(boundaries:Rectangle) {
			super(boundaries);
		}
		
		//------------------------------------------------------------------------------------------------
		// Public methods
		//------------------------------------------------------------------------------------------------
		
		/**
		 * @inheritDoc
		 */
		override public function init():void {
			super.init();
			
			var grid:IGrid = this.tileGrid;
			var m1:Number = grid.cellWidth * this.tileSettings.sectionWidth;
			var m2:Number = grid.cellHeight * this.tileSettings.sectionHeight;
			var w:Number = m1 > m2 ? m2 : m1;
			
			this._dataGrid = new DataGrid(this.globalBoundaries, w, w, this.rectFunction);
		}
		
		/**
		 * @inheritDoc
		 */
		override public function getArea(rect:Rectangle, filter:Function = null):IMapArea {
			if (!this._dataGrid.rect.intersects(rect)) {
				return null;
			}
		
			var area:SimpleMapArea = new SimpleMapArea();
			var sections:Array = [];
			var objects:Array = [];
			var result:Array = this._dataGrid.getObjects(rect);
			var nodes:Array = [];			
			
			for each(var o:ISceneObject in result) {
				if (o is ISceneNode) {
					var sceneNode:ISceneNode = o as ISceneNode;
					for each(var node:MapNode in sceneNode.tiles) {
						nodes.push(node);
					}
					sections.push(o);
					
					for each(var sceneObject:ISceneObject in sceneNode.objects) {
						if (filter == null || filter(o)) {
							objects.push(sceneObject)
						}
					}
				}else {
					if (filter == null || filter(o)) {
						objects.push(o);
					}
				}
			}
			
			area.rect = rect.clone();
			area.nodes = nodes;
			area.objects = objects;
			area.sections = sections;
			area.sectionWidth = this.tileSettings.sectionWidth;
			area.sectionHeight = this.tileSettings.sectionHeight;
			
			return area;
		}
		
		/**
		 * @inheritDoc
		 */
		override public function getObjects(rect:Rectangle, filter:Function = null):Array {
			return this._dataGrid.getObjects(rect, filter);
		}
		
		/**
		 * @inheritDoc
		 */
		override public function dispose():void {
			super.dispose();
			
			this._dataGrid.clear();
		}
		
		//------------------------------------------------------------------------------------------------
		// Protected methods
		//------------------------------------------------------------------------------------------------
		
		/**
		 * @inheritDoc
		 */
		override protected function addObjectToStage(o:ISceneObject):Boolean {
			this.updateObjectProperties(o);
			return this._dataGrid.put(o);
		}
		
		/**
		 * @inheritDoc
		 */
		override protected function updateObjectAtStage(o:ISceneObject):Boolean {
			this.updateObjectProperties(o);
			return this._dataGrid.put(o)
		}
		
		/**
		 * @inheritDoc
		 */
		override protected function removeObjectFromStage(o:ISceneObject):Boolean {
			return this._dataGrid.remove(o);
		}
		
		/**
		 * @inheritDoc
		 */
		override protected function findNodes(rect:Rectangle):Array {
			return this._dataGrid.getObjects(rect, this.sceneNodesOnlyFilter);
		}
		
		protected function sceneNodesOnlyFilter(o:ISceneObject):Boolean {
			return o is ISceneNode;
		}
		
		protected function rectFunction(object:Object):Rectangle {
			var sceneObject:ISceneObject = object as ISceneObject;
			
			return sceneObject.bounds.rect;
		}
	}

}